In [88]:
    
from array import array
import reprlib
import math
import numbers
import functools
import operator
import itertools
class Vector:
    typecode = 'd'
    
    def __init__(self, components):
        self._components = array(self.typecode, components)
        
    def __iter__(self):
        return iter(self._components)
    
    def __repr__(self):
        components = reprlib.repr(self._components)
        components = components[components.find('['):-1]
        return 'Vector({})'.format(components)
    
    def __str__(self):
        return str(tuple(self))
    
    def __bytes__(self):
        return (bytes([ord(self.typecode)]) +
               bytes(self._components))
    
    def __eq__(self, other):
        return (len(self) == len(other) and
                all(a == b for a, b in zip(self, other)))
    
    def __hash__(self):
        hashes = (hash(x) for x in self._components)
        return functools.reduce(operator.xor, hashes, 0)
    
    def __abs__(self):
        return math.sqrt(sum(x * x) for x in self)
    
    def __bool__(self):
        return bool(abs(self))
    
    def __len__(self):
        return len(self._components)
    
    def __getitem__(self, index):
        cls = type(self)
        if isinstance(index, slice):
            return cls(self._components[index])
        elif isinstance(index, numbers.Integral):
            return self._components[index]
        else:
            msg = '{.__name__} indices must be integers'
            raise TypeError(msg.format(cls))
            
    shortcut_names = 'xyzt'
    
    def __getattr__(self, name):
        cls = type(self)
        if len(name) == 1:
            pos = cls.shortcut_names.find(name)
            if 0 <= pos < len(self._components):
                return self._components[pos]
        msg = '{.__name__!r} object has no atttribute {!r}'
        raise AttributeError(msg.format(cls, name))
        
    def __setattr__(self, name, value):
        cls = type(self)
        if len(name) == 1:
            if name in cls.shortcut_names:
                error = 'readonly attribute {attr_name!r}'
            elif name.islower():
                error = "can't set attributes 'a' to 'z' in {cls_name!r}"
            else:
                error = ''
            if error:
                msg = error.format(cls_name=cls.__name__, attr_name=name)
                raise AttributeError(msg)
        super().__setattr__(name, value)
    
    def angle(self, n):
        r = math.sqrt(sum(x * x for x in self[n:]))
        a = math.atan2(r, self[n-1])
        if (n == len(self) - 1) and (self[-1] < 0):
            return math.pi * 2 - a
        else:
            return a
        
    def angles(self):
        return (self.angle(n) for n in range(1, len(self)))
    
    def __format__(self, fmt_spec=''):
        if fmt_spec.endswith('h'): # hyperspherical coordinates
            fmt_spec = fmt_spec[:-1]
            coords = itertools.chain([abs(self)], self.angles())
            outer_fmt = '<{}>'
        else:
            coords = self
            outer_fmt = '({})'
        components = (format(c, fmt_spec) for c in coords)
        return outer_fmt.format(', '.join(components))
        
    @classmethod
    def frombytes(cls, octets):
        typecode = chr(octets[0])
        memv = memoryview(octets[1:]).cast(typecode)
        return cls(memv)
    
In [2]:
    
Vector([3.1, 4.2])
    
    Out[2]:
In [3]:
    
Vector((3, 4, 5))
    
    Out[3]:
In [4]:
    
Vector(range(10))
    
    Out[4]:
In [8]:
    
v1 = Vector([3, 4, 5])
    
In [9]:
    
len(v1)
    
    Out[9]:
In [10]:
    
v1[0], v1[-1]
    
    Out[10]:
In [11]:
    
v7 = Vector(range(7))
v7[1:4]
    
    Out[11]:
In [12]:
    
class MySeq:
    def __getitem__(self, index):
        return index
    
In [13]:
    
s = MySeq()
s[1]
    
    Out[13]:
In [14]:
    
s[1:4]
    
    Out[14]:
In [16]:
    
s[1:4:2]
    
    Out[16]:
In [17]:
    
s[1:4:2, 9]
    
    Out[17]:
In [18]:
    
s[1:4:2, 7:9]
    
    Out[18]:
In [19]:
    
slice
    
    Out[19]:
In [21]:
    
print(dir(slice))
    
    
In [22]:
    
help(slice.indices)
    
    
In [23]:
    
slice(None, 10, 2).indices(5)
    
    Out[23]:
In [24]:
    
slice(-3, None, None).indices(5)
    
    Out[24]:
In [39]:
    
v7 = Vector(range(7))
    
In [40]:
    
v7[-1]
    
    Out[40]:
In [41]:
    
v7[1:4]
    
    Out[41]:
In [42]:
    
v7[-1:]
    
    Out[42]:
In [43]:
    
v7[1,2]
    
    
In [45]:
    
v = Vector(range(10))
v.x
    
    Out[45]:
In [46]:
    
v.y, v.z, v.t
    
    Out[46]:
In [69]:
    
v = Vector([0.0, 1.0, 2.0, 3.0, 4.0])
v.x
    
    Out[69]:
In [71]:
    
v.x = 10
    
    
In [65]:
    
v
    
    Out[65]:
In [72]:
    
2 * 3 * 4 * 5
    
    Out[72]:
In [73]:
    
import functools
    
In [75]:
    
functools.reduce(lambda a,b: a*b, range(1,6))
    
    Out[75]:
In [76]:
    
n = 0
for i in range(1, 6):
    n ^= i
    
In [77]:
    
n
    
    Out[77]:
In [78]:
    
functools.reduce(lambda a, b: a^b, range(6))
    
    Out[78]:
In [79]:
    
import operator
    
In [80]:
    
functools.reduce(operator.xor, range(6))
    
    Out[80]:
In [95]:
    
v = Vector((float(x) for x in range(1,10)))
    
In [96]:
    
v
    
    Out[96]:
In [97]:
    
format(v,'h')
    
    
In [ ]: